# Definition and implementation of a single curve.

itcl::class Curve {

	proc lreverse {list} {
		set rlist {}
		foreach el $list {
			set rlist [concat [list $el] $rlist]
		}
		return $rlist
	}

	proc isSamePoint {pt1 pt2} {
		expr ([lindex $pt1 0] == [lindex $pt2 0]) && ([lindex $pt1 1] == [lindex $pt2 1])
	}
	proc minPoint {pt1 pt2} {
		list [expr ([lindex $pt1 0] <= [lindex $pt2 0]) ? [lindex $pt1 0] : [lindex $pt2 0]] \
			[expr ([lindex $pt1 1] <= [lindex $pt2 1]) ? [lindex $pt1 1] : [lindex $pt2 1]]
	}
	proc maxPoint {pt1 pt2} {
		list [expr ([lindex $pt1 0] >= [lindex $pt2 0]) ? [lindex $pt1 0] : [lindex $pt2 0]] \
			[expr ([lindex $pt1 1] >= [lindex $pt2 1]) ? [lindex $pt1 1] : [lindex $pt2 1]]
	}

	constructor {points {operators {}}} {
		if {([llength $operators] == 0) && ([llength $points] > 1)} {
			set iPoints [::list $points]
		} else {
			set iPoints $points
		}
		set iOperators $operators
	}

	method lineTo {pt} {
		lappend iPoints $pt
		lappend iOperators "l"
		return
	}
	method curveTo {pt1 pt2 pt} {
		lappend iPoints $pt1 $pt2 $pt
		lappend iOperators "c"
		return
	}

	method connect {curve} {
		if {[isSamePoint [getEndPoint] [uplevel $curve getStartPoint]]} {
			uplevel [list Curve #auto [concat $iPoints [lrange [uplevel $curve cget -iPoints] 1 end]] [concat $iOperators [uplevel $curve cget -iOperators]]]
		} elseif {[isSamePoint [getStartPoint] [uplevel $curve getEndPoint]]} {
			uplevel [list Curve #auto [concat [uplevel $curve cget -iPoints] [lrange $iPoints 1 end]] [concat [uplevel $curve cget -iOperators] $iOperators]]]
		} elseif {[isSamePoint [getEndPoint] [uplevel $curve getEndPoint]]} {
			uplevel [list Curve #auto [concat $iPoints [Curve::lreverse [lrange [uplevel $curve cget -iPoints] 1 end]]] [concat $iOperators [lreverse [uplevel $curve cget -iOperators]]]
		} elseif {[isSamePoint [getStartPoint] [uplevel $curve getStartPoint]]} {
			uplevel [list Curve #auto [concat [lreverse [uplevel $curve cget -iPoints]] [lrange $iPoints 1 end]] [concat [lreverse [uplevel $curve cget -iOperators]] $iOperators]]
		} else {
			return 0
		}
	}
	method connectSelf {curve} {
		if [isSamePoint [getEndPoint] [uplevel $curve getStartPoint]] {
			set iPoints [concat $iPoints [lrange [uplevel $curve cget -iPoints] 1 end]]
			set iOperators [concat $iOperators [uplevel $curve cget -iOperators]]
		} elseif [isSamePoint [getStartPoint] [uplevel $curve getEndPoint]] {
			set iPoints [concat [uplevel $curve cget -iPoints] [lrange $iPoints 1 end]]
			set iOperators [concat [uplevel $curve cget -iOperators] $iOperators]
		} elseif [isSamePoint [getEndPoint] [uplevel $curve getEndPoint]] {
			set iPoints [concat $iPoints [lreverse [lrange [uplevel $curve cget -iPoints] 1 end]]]
			set iOperators [concat $iOperators [lreverse [uplevel $curve cget -iOperators]]]
		} elseif [isSamePoint [getStartPoint] [uplevel $curve getStartPoint]] {
			set iPoints [concat [lreverse [uplevel $curve cget -iPoints]] [lrange $iPoints 1 end]]
			set iOperators [concat [lreverse [uplevel $curve cget -iOperators]] $iOperators]
		} else {
			return 0
		}
		return 1
	}
	method transform {tfr args} {
		set points {}
		foreach pt $iPoints {
			lappend points [uplevel [list $tfr $pt] $args]
		}
		Curve #auto $points $iOperators
	}
	method transformSelf {tfr args} {
		set points {}
		foreach pt $iPoints {
			lappend points [uplevel [list $tfr $pt] $args]
		}
		set iPoints $points
		return
	}
	method reverse {} {
		Curve #auto [lreverse $iPoints] [lreverse $iOperators]
	} 
	method reverseSelf {} {
		set iPoints [lreverse $iPoints]
		set iOperators [lreverse $iOperators]
		return
	}

	method getStartPoint {} {
		lindex $iPoints 0
	}
	method getEndPoint {} {
		lindex $iPoints end
	}
	method getPoints {} {
		return $iPoints
	}
	method getBBox {} {
		set minPoint [lindex $iPoints 0]
		set maxPoint $minPoint
		foreach point $iPoints {
			set minPoint [minPoint $minPoint $point]
			set maxPoint [maxPoint $maxPoint $point]
		}
		concat $minPoint $maxPoint
	}
	method isClosed {} {
		isSamePoint [getStartPoint] [getEndPoint]
	}
	method forEachPiece {pieceName lineToBody curveToBody} {
		upvar $pieceName piece 
		set idx 0
		foreach op $iOperators {
			switch $op "l" {
				set piece [lindex $iPoints [incr idx]]
				uplevel $lineToBody
			} "c" {
				set piece [lrange $iPoints [incr idx] [incr idx 2]]
				uplevel $curveToBody
			}
		}
	}

	public variable iPoints
	public variable iOperators
}
